package com.javastudy.apache.collection;

import static org.junit.Assert.*;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.ListResourceBundle;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeMap;
import java.util.List;
import java.util.ArrayList;

//import junit.framework.Test;

import org.apache.commons.collections4.Factory;
import org.apache.commons.collections4.FactoryUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.TransformerUtils;
//import org.apache.commons.collections4.collection.TransformedCollectionTest;
import org.apache.commons.collections4.keyvalue.DefaultKeyValue;
import org.apache.commons.collections4.keyvalue.DefaultMapEntry;
import org.apache.commons.collections4.map.HashedMap;
import org.apache.commons.collections4.map.LazyMap;
import org.apache.commons.collections4.map.MultiValueMap;
import org.apache.commons.collections4.map.PredicatedMap;
import org.junit.Test;

public class MapUtilsTest extends BulkTest {

	public MapUtilsTest(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	//有如下方法
	private void listAllMethods(){
//		MapUtils.debugPrint(out, label, map);
//		MapUtils.emptyIfNull(map);
//		MapUtils.fixedSizeMap(map);
//		MapUtils.fixedSizeSortedMap(map);
//		//可以get各种类型的值
//		MapUtils.getBoolean(map, key);
//		MapUtils.getBoolean(map, key, defaultValue);
//		MapUtils.getBooleanValue(map, key);
//		MapUtils.getBooleanValue(map, key, defaultValue);
//		MapUtils.getByte(map, key);
//		MapUtils.getByte(map, key, defaultValue);
//		MapUtils.getByteValue(map, key);
//		MapUtils.getByteValue(map, key, defaultValue);
//		MapUtils.getDouble(map, key);
//		MapUtils.invertMap(map);
//		MapUtils.isEmpty(map);
//		MapUtils.isNotEmpty(map);
//		MapUtils.iterableMap(map);
//		MapUtils.multiValueMap(map);
//		MapUtils.verbosePrint(out, label, map);
//		MapUtils.unmodifiableSortedMap(map);
//		MapUtils.unmodifiableMap(map);
//		MapUtils.transformedSortedMap(map, keyTransformer, valueTransformer);
//		MapUtils.transformedMap(map, keyTransformer, valueTransformer);
//		MapUtils.toProperties(map);
//		MapUtils.toMap(resourceBundle);
//		MapUtils.synchronizedSortedMap(map);
//		MapUtils.synchronizedMap(map);
//		MapUtils.safeAddToMap(map, key, value);
//		MapUtils.putAll(map, array);
//		MapUtils.predicatedMap(map, keyPred, valuePred);
//		MapUtils.predicatedSortedMap(map, keyPred, valuePred);
//		MapUtils.populateMap(map, elements, keyTransformer, valueTransformer);
//		MapUtils.populateMap(map, elements, keyTransformer);
//		MapUtils.orderedMap(map);
//		MapUtils.multiValueMap(map, collectionFactory)
//		MapUtils.lazyMap(map, factory);
//		MapUtils.lazySortedMap(map, factory);
		
	}	
	// public static Test suite() {
	// return BulkTest.makeSuite(MapUtilsTest.class);
	// }

	public Predicate<Object> getPredicate() {
		return new Predicate<Object>() {
			public boolean evaluate(final Object o) {
				return o instanceof String;
			}
		};
	}

	/**
	 * 不清楚.好像是对map的key/value类型进行检查，只有符合类型的才能放进去，不然出错。
	 * 感觉除非key/value的类型都为Object情况下才比较有用，如果是指定具体类型的map，实际上是没多少用处的
	 */
	@Test
	public void testPredicatedMap() {
		final Predicate<Object> p = getPredicate();
		Map<Object, Object> map = MapUtils.predicatedMap(new HashMap<Object, Object>(), p, p);
		assertTrue("returned object should be a PredicatedMap", map instanceof PredicatedMap);
		map.put("key", "value");//OK
		//map.put("integer",new Integer(100));//wrong - java.lang.IllegalArgumentException: Cannot add value - Predicate rejected it
		try {
//			MapUtils.predicatedMap(null, p, p);
//			fail("Expecting IllegalArgumentException for null map.");
		} catch (final IllegalArgumentException e) {
			// expected
		}
	}

	/**
	 * 不是很清楚怎麼用，但是下面注释掉的代码都会抛NPE异常，这是测试期望的。
	 * 经过测试发现，factory是用来构造value的，无论map输入什么key，其对应的值都是由factory来提供的 但是不知道应用场景是什么
	 */
	@Test
	public void testLazyMapFactory() {
		final Factory<Integer> factory = FactoryUtils.constantFactory(Integer.valueOf(5));//
		Map<Object, Object> map = MapUtils.lazyMap(new HashMap<Object, Object>(), factory);
		assertTrue(map instanceof LazyMap); // 这个时候，该map仍然为空
		System.out.println(map.get("keyHasValue?"));
		// try {
		// map = MapUtils.lazyMap(new HashMap<Object, Object>(),
		// (Factory<Object>) null);
		// fail("Expecting IllegalArgumentException for null factory");
		// } catch (final IllegalArgumentException e) {
		// // expected
		// }

		// try {
		// map = MapUtils.lazyMap((Map<Object, Object>) null, factory);
		// fail("Expecting IllegalArgumentException for null map");
		// } catch (final IllegalArgumentException e) {
		// // expected
		// }

		final Transformer<Object, Integer> transformer = TransformerUtils.asTransformer(factory);
		map = MapUtils.lazyMap(new HashMap<Object, Object>(), transformer);
		assertTrue(map instanceof LazyMap);
		System.out.println(map.get("anotherKeyHasValue?"));
		// try {
		// map = MapUtils.lazyMap(new HashMap<Object, Object>(),
		// (Transformer<Object, Object>) null);
		// fail("Expecting IllegalArgumentException for null transformer");
		// } catch (final IllegalArgumentException e) {
		// // expected
		// }
		// try {
		// map = MapUtils.lazyMap((Map<Object, Object>) null, transformer);
		// fail("Expecting IllegalArgumentException for null map");
		// } catch (final IllegalArgumentException e) {
		// // expected
		// }
	}

	/**
	 * 主要是把一维数组/多维数组/MapEntry/KeyValue对象转换成Map对象 需要了解MapEntry和KeyValue这两类对象的使用
	 */
	@Test
	public void testPutAll_Map_array() {
		try {
			MapUtils.putAll(null, null);
			fail();
		} catch (final NullPointerException ex) {
		}
		try {
			MapUtils.putAll(null, new Object[0]);
			fail();
		} catch (final NullPointerException ex) {
		}

		Map<String, String> test = MapUtils.putAll(new HashMap<String, String>(), new String[0]);
		assertEquals(0, test.size());

		// sub array 多维数组
		test = MapUtils.putAll(new HashMap<String, String>(),
				new String[][] { { "RED", "#FF0000" }, { "GREEN", "#00FF00" }, { "BLUE", "#0000FF" } });
		assertEquals(true, test.containsKey("RED"));
		assertEquals("#FF0000", test.get("RED"));
		assertEquals(true, test.containsKey("GREEN"));
		assertEquals("#00FF00", test.get("GREEN"));
		assertEquals(true, test.containsKey("BLUE"));
		assertEquals("#0000FF", test.get("BLUE"));
		assertEquals(3, test.size());

		try {
			MapUtils.putAll(new HashMap<String, String>(),
					new String[][] { { "RED", "#FF0000" }, null, { "BLUE", "#0000FF" } });
			fail();
		} catch (final IllegalArgumentException ex) {
		}

		try {
			MapUtils.putAll(new HashMap<String, String>(),
					new String[][] { { "RED", "#FF0000" }, { "GREEN" }, { "BLUE", "#0000FF" } });
			fail();
		} catch (final IllegalArgumentException ex) {
		}

		try {
			MapUtils.putAll(new HashMap<String, String>(),
					new String[][] { { "RED", "#FF0000" }, {}, { "BLUE", "#0000FF" } });
			fail();
		} catch (final IllegalArgumentException ex) {
		}

		/////////////// flat array 一维数组
		test = MapUtils.putAll(new HashMap<String, String>(),
				new String[] { "RED", "#FF0000", "GREEN", "#00FF00", "BLUE", "#0000FF" });
		assertEquals(true, test.containsKey("RED"));
		assertEquals("#FF0000", test.get("RED"));
		assertEquals(true, test.containsKey("GREEN"));
		assertEquals("#00FF00", test.get("GREEN"));
		assertEquals(true, test.containsKey("BLUE"));
		assertEquals("#0000FF", test.get("BLUE"));
		assertEquals(3, test.size());

		test = MapUtils.putAll(new HashMap<String, String>(),
				new String[] { "RED", "#FF0000", "GREEN", "#00FF00", "BLUE", "#0000FF", "PURPLE" // ignored
				});
		assertEquals(true, test.containsKey("RED"));
		assertEquals("#FF0000", test.get("RED"));
		assertEquals(true, test.containsKey("GREEN"));
		assertEquals("#00FF00", test.get("GREEN"));
		assertEquals(true, test.containsKey("BLUE"));
		assertEquals("#0000FF", test.get("BLUE"));
		assertEquals(3, test.size());

		// map entry需要注意MapEntry的使用
		test = MapUtils.putAll(new HashMap<String, String>(),
				new Object[] { new DefaultMapEntry<String, String>("RED", "#FF0000"),
						new DefaultMapEntry<String, String>("GREEN", "#00FF00"),
						new DefaultMapEntry<String, String>("BLUE", "#0000FF") });
		assertEquals(true, test.containsKey("RED"));
		assertEquals("#FF0000", test.get("RED"));
		assertEquals(true, test.containsKey("GREEN"));
		assertEquals("#00FF00", test.get("GREEN"));
		assertEquals(true, test.containsKey("BLUE"));
		assertEquals("#0000FF", test.get("BLUE"));
		assertEquals(3, test.size());

		// key value
		test = MapUtils.putAll(new HashMap<String, String>(),
				new Object[] { new DefaultKeyValue<String, String>("RED", "#FF0000"),
						new DefaultKeyValue<String, String>("GREEN", "#00FF00"),
						new DefaultKeyValue<String, String>("BLUE", "#0000FF") });
		assertEquals(true, test.containsKey("RED"));
		assertEquals("#FF0000", test.get("RED"));
		assertEquals(true, test.containsKey("GREEN"));
		assertEquals("#00FF00", test.get("GREEN"));
		assertEquals(true, test.containsKey("BLUE"));
		assertEquals("#0000FF", test.get("BLUE"));
		assertEquals(3, test.size());
	}

	/**
	 * 需要了解ResourceBundle到底是怎么用的
	 */
	@Test
	public void testConvertResourceBundle() {
		final Map<String, String> in = new HashMap<String, String>(5, 1);
		in.put("1", "A");
		in.put("2", "B");
		in.put("3", "C");
		in.put("4", "D");
		in.put("5", "E");

		final ResourceBundle b = new ListResourceBundle() {
			@Override
			public Object[][] getContents() {
				final Object[][] contents = new Object[in.size()][2];
				final Iterator<String> i = in.keySet().iterator();
				int n = 0;
				while (i.hasNext()) {
					final Object key = i.next();
					final Object val = in.get(key);
					contents[n][0] = key;
					contents[n][1] = val;
					++n;
				}
				return contents;
			}
		};

		final Map<String, Object> out = MapUtils.toMap(b);

		assertTrue(in.equals(out));
	}

	/**
	 * 不知有何用
	 */
	@Test
	public void testDebugAndVerbosePrintCasting() {
		final Map<Integer, String> inner = new HashMap<Integer, String>(2, 1);
		inner.put(2, "B");
		inner.put(3, "C");

		final Map<Integer, Object> outer = new HashMap<Integer, Object>(2, 1);
		outer.put(0, inner);
		outer.put(1, "A");

		final ByteArrayOutputStream out = new ByteArrayOutputStream();
		final PrintStream outPrint = new PrintStream(out);

		try {
			MapUtils.debugPrint(outPrint, "Print Map", outer);//here
			System.out.println("done");
		} catch (final ClassCastException e) {
			fail("No Casting should be occurring!");
		}
	}

	/**
	 * 把数组的值放到map里头，奇数为key,偶数为value，需要注意最后一组可能只有key，没有value。这个时候key对应的值为null.
	 * 如下面lastKey对应的值就是为null.
	 */
	@Test
	public void testPutAll() {
		Map<String, String> arg0 = new HashedMap<String, String>();
		String[] arg1 = new String[] { "RED", "#FF0000", "GREEN", "#00FF00", "BLUE", "#0000FF", "lastKey" };
		MapUtils.putAll(arg0, arg1);
		System.out.println(arg0.get("RED"));
		System.out.println(arg0.get("lastKey"));
	}

	/**
	 * key-value互相对调
	 */
	@Test
	public void testInvertMap() {
		final Map<String, String> in = new HashMap<String, String>(5, 1);
		in.put("1", "A");
		in.put("2", "B");
		in.put("3", "C");
		in.put("4", "D");
		in.put("5", "E");

		final Set<String> inKeySet = new HashSet<String>(in.keySet());
		final Set<String> inValSet = new HashSet<String>(in.values());

		final Map<String, String> out = MapUtils.invertMap(in);

		final Set<String> outKeySet = new HashSet<String>(out.keySet());
		final Set<String> outValSet = new HashSet<String>(out.values());

		assertTrue(inKeySet.equals(outValSet));
		assertTrue(inValSet.equals(outKeySet));

		assertEquals("1", out.get("A"));
		assertEquals("2", out.get("B"));
		assertEquals("3", out.get("C"));
		assertEquals("4", out.get("D"));
		assertEquals("5", out.get("E"));
	}
	


}
